package main

import (
	"fmt"
	"goProject/foundationStudy/unitTenObject/model"
)

/**
	结构体语法：必须使用：struct关键字
	type 类型名 struct {
    字段1 类型1
    字段2 类型2
}
*/
// 定义老师结构体，将老师中的各个属性  统一放入结构体中管理：
type Teacher struct {
	//变量名字大写外界可以访问这个属性
	Name   string
	Age    int
	School string
}

type Student struct {
	Age int
}

// 结构体进行type重新定义(相当于取别名)，Golang认为是新的数据类型，但是相互间可以强转
type Stu Student

type Person struct {
	Age int
}

// 定义动物结构体：
type Animal struct {
	Age    int
	Weight float64
}

// 定义结构体：Cat
type Cat struct {
	//为了复用性，体现继承思维，嵌入匿名结构体：——》将Animal中的字段和方法都达到复用
	Animal
}

// 给Animal绑定方法：喊叫：
func (an *Animal) Shout() {
	fmt.Println("我可以大声喊叫")
}

// 给Animal绑定方法：自我展示：
func (an *Animal) ShowInfo() {
	fmt.Printf("动物的年龄是：%v,动物的体重是：%v \n", an.Age, an.Weight)
}

// 对Cat绑定特有的方法：
func (c *Cat) scratch() {
	fmt.Println("我是小猫，我可以挠人")
}

//接口的定义：定义规则、定义规范，定义某种能力：
/**
	接口语法：必须使用：interface关键字
	type 名称 interface {
	方法名()
}
*/
type SayHello interface {
	sayHello()
}

// 接口的实现：定义一个结构体： 中国人：
type chinese struct {
}

// 实现接口的方法---》具体的实现：
func (person *chinese) sayHello() {
	fmt.Println("你好")
}

// 接口的实现：定义一个结构体： 美国人：
type American struct {
}

// 实现接口的方法---》具体的实现：
func (person American) sayHello() {
	fmt.Println("hi")
}

// 定义一个函数：专门用来各国人打招呼的函数，接收具备SayHello接口的能力的变量：
func greet(s SayHello) {
	s.sayHello()

}

// 只要是自定义数据类型，就可以实现接口，不仅仅是结构体类型。
type integer int

func (i integer) sayHello() {
	fmt.Println("sat hi + ", i)
}

type CInterface interface {
	c()
}
type BInterface interface {
	b()
}
type AInterface interface {
	a()
	BInterface
	CInterface
}
type face struct {
}

func (s *face) a() {
	fmt.Println("a")
}
func (s *face) b() {
	fmt.Println("b")
}
func (s *face) c() {
	fmt.Println("c")
}

func main() {
	//面向对象
	fmt.Println()
	fmt.Println("面向对象==============================================")

	// 第一种方式
	//创建老师结构体的实例、对象、变量：
	var t1 Teacher
	fmt.Println(t1)
	t1.Name = "马士兵"
	t1.Age = 45
	t1.School = "清华大学"
	fmt.Println(t1)
	fmt.Println(t1.Age + 10)

	fmt.Println("第二种方式==============================================")
	fmt.Println()
	// 需按照顺序进行赋值操作
	var t2 Teacher = Teacher{"晨", 27, "家里蹲"}
	fmt.Println(t2)
	fmt.Println()

	fmt.Println("第三种方式==============================================")
	fmt.Println()
	// 返回结构体指针
	//创建老师结构体的实例、对象、变量：
	var t3 *Teacher = new(Teacher)
	// t3是指针，t3其实指向的就是地址，应该给这个地址的指向的对象的字段赋值
	(*t3).Name = "马士兵"
	(*t3).Age = 45 //*作用根据地址取地址值
	// 简化赋值方式 go编译器底层对t3.School转化(*t3).School = "清华大学"
	t3.School = "清华大学"
	fmt.Println(*t3)
	fmt.Println()

	fmt.Println("第四种方式==============================================")
	fmt.Println()
	//返回结构体指针
	//&	取址符	获取变量的内存地址	ptr := &variable
	//*	解引用符	访问指针指向的内存值（或声明指针类型）	value := *ptr
	var t4 *Teacher = &Teacher{
		"晨",
		27,
		"家里蹲",
	}
	fmt.Println(*t4)
	fmt.Println()

	fmt.Println("结构体之间转换==============================================")
	fmt.Println()
	var s Student = Student{10}
	var p Person = Person{10}
	// 不能通过赋值的形式 只能进行强制转换
	s = Student(p)
	fmt.Println(s)
	fmt.Println(p)

	var s1 Student = Student{19}
	var s2 Stu = Stu{19}
	s1 = Student(s2)
	fmt.Println(s1)
	fmt.Println(s2)
	fmt.Println()

	fmt.Println("方法引入==============================================")
	fmt.Println()
	var per Person
	per.Age = 100
	per.test()
	fmt.Println(per)

	var per1 Person
	per1.Age = 600
	fmt.Printf("per1 的地址是：%p", &p)
	(&per1).test1()
	fmt.Println(per1.Age)
	fmt.Println()

	fmt.Println("方法就、和函数区别==============================================")
	fmt.Println()
	//方法：需要绑定指定数据类型
	//函数：不需要绑定数据类型
	//函数的调用方式: 函数名(实参列表)
	//方法的调用方式：变量.方法名(实参列表)
	s = Student{60}
	method(s)
	s.test02()

	//对于函数来说，参数类型对应是什么就要传入什么。
	var stu2 Student = Student{890}
	method(stu2)
	//method01(&s)错误
	method01(&stu2)
	//method02(s)错误

	//对于方法来说，接收者为值类型，可以传入指针类型，接受者为指针类型，可以传入值类型。
	var stu3 = Student{1234567}
	stu3.test02()
	(&stu3).test02()

	(&stu3).test03()
	stu3.test03()
	fmt.Println()

	fmt.Println("创建结构体实力指定字段==============================================")
	fmt.Println()
	// 按照指定类型进行估值
	var tea1 Teacher = Teacher{
		Name:   "丽丽",
		Age:    26,
		School: "123e",
	}
	fmt.Println(tea1)

	//返回结构体指针类型
	var tea2 *Teacher = &Teacher{"明明", 27, "家里蹲"}
	fmt.Println(*tea2)
	var tea3 *Teacher = &Teacher{
		School: "明明",
		Age:    27,
		Name:   "家里蹲",
	}
	fmt.Println(*tea3)
	fmt.Println()

	fmt.Println("挎包创建实例==============================================")
	fmt.Println()
	// 跨包创建实例
	models := model.Student{Name: "丽丽", Age: 90}
	fmt.Println(models)

	persons := model.NewPerson("丽丽", 100)
	fmt.Println(persons)
	fmt.Println()

	fmt.Println("封装==============================================")
	fmt.Println()
	newPer := model.NewPersons("k")
	newPer.SetAge(22)
	fmt.Println(newPer.Name)
	fmt.Println(newPer.GetAge())
	fmt.Println(*newPer)
	fmt.Println()

	fmt.Println("继承==============================================")
	fmt.Println()
	//创建Cat结构体示例：
	cat := &Cat{}
	cat.Age = 13
	cat.Weight = 10.6
	cat.Shout()
	cat.ShowInfo()
	cat.scratch()
	fmt.Println()

	fmt.Println("接口==============================================")
	fmt.Println()
	//创建一个中国人：
	c := &chinese{}
	//创建一个美国人：
	a := American{}
	//美国人打招呼：
	greet(a)
	//中国人打招呼：
	greet(c)
	//接口本身不能创建实例，但是可以指向一个实现了该接口的自定义类型的变量。
	// var s SayHello
	// s.sayHello
	var i integer = 10
	var i1 SayHello = i
	i1.sayHello()

	//一个接口(比如A接口)可以继承多个别的接口(比如B,C接口)，这时如果要实现A接口,也必须将B,C接口的方法也全部实现
	var fa *face
	var in AInterface = fa
	in.a()
	in.b()
	in.c()
	fmt.Println()

	fmt.Println("断言==============================================")
	fmt.Println()

	ch := model.Chinese{}
	fmt.Println(ch)
	model.Greet(ch)

	stu := &model.Student{Name: "link", Age: 123}
	by := model.ToJson(stu)
	stud := &model.Student{}
	model.JsonString(by, stud)
	fmt.Println(stud.Age)
}

// （1）func (p Person)test()相当于A结构体有一个方法叫test
// (p Person)体现方法test和结构体p绑定关系
func (p Person) test() {
	p.Age = 200
	fmt.Println(p.Age)
}

func (p *Person) test1() {
	(*p).Age = 300
	fmt.Println((*p).Age)
}

// 定义方法
func (s Student) test02() {
	fmt.Println(s.Age)
}

func (s *Student) test03() {
	fmt.Println((*s).Age)
}

// 定义函数
func method(s Student) {
	fmt.Println(s.Age)
}

func method01(s *Student) {
	fmt.Println((*s).Age)
}
